package com.djinquan.common.util;

import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * SpelUtils
 *
 * @author kancy
 * @date 2022/8/27 20:51
 */
public class SpelUtil {

    /**
     * 用于SpEL表达式解析.
     */
    private static final SpelExpressionParser parser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private static final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 解析
     *
     * @param spel        spel
     * @param params      参数 map或者实体
     * @param returnClass 返回类
     * @return {@link T}
     */
    public static <T> T parse(String spel, Object params, Class<T> returnClass){
        return TypeUtils.cast(parse(spel, params), returnClass, ParserConfig.getGlobalInstance());
    }

    /**
     * 解析
     *
     * @param spel   spel
     * @param params 参数 map或者实体
     * @return {@link Object}
     */
    public static Object parse(String spel, Object params){
        // 解析过后的Spring表达式对象
        Expression expression = parser.parseExpression(spel);
        // spring的表达式上下文对象
        EvaluationContext context = new StandardEvaluationContext(params);

        if (ClassUtils.isPrimitiveOrWrapper(params.getClass())
                || params instanceof String
                || params instanceof Collection){
            return expression.getValue(context);
        }

        if (params instanceof Map){
            // 这里很关键，如果没有配置MapAccessor，那么只能用['c']['a']这种解析方式
            context.getPropertyAccessors().add(new MapAccessor());
        } else {
            if (!params.getClass().getName().startsWith("java.")){
                BeanMap beanMap = BeanMap.create(params);
                // 设置变量
                beanMap.forEach((k,v) -> context.setVariable(String.valueOf(k), v));
            }
        }

        return expression.getValue(context);
    }

    /**
     * 通过spel生成key
     *
     * @param spELString spel
     * @param method     方法
     * @param args       参数
     * @return {@link String}
     */
    public static String generateKeyBySpEL(String spELString, Method method, Object[] args) {
        // 解析过后的Spring表达式对象
        Expression expression = parser.parseExpression(spELString);

        // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
        String[] paramNames = nameDiscoverer.getParameterNames(method);

        // 初始化上下文
        if (Objects.isNull(paramNames) || method.getParameterCount() <= 0 || Objects.isNull(args) || args.length <= 0){
            return String.valueOf(expression.getValue());
        }

        // spring的表达式上下文对象
        EvaluationContext context;
        if (method.getParameterCount() == 1 && Objects.nonNull(args[0])){
            // 一个EvaluationContext只能有一个RootObject，引用它的属性时，可以不加前缀
            context = new StandardEvaluationContext(args[0]);
            if (args[0] instanceof Map){
                // 这里很关键，如果没有配置MapAccessor，那么只能用['c']['a']这种解析方式
                context.getPropertyAccessors().add(new MapAccessor());
            }
            context.setVariable(paramNames[0], args[0]);
        } else {
            Map<String, Object> rootObject = new HashMap<>();
            for (int i = 0; i < method.getParameterCount(); i++) {
                rootObject.put(paramNames[i], args[i]);
            }
            context = new StandardEvaluationContext(rootObject);
            // 这里很关键，如果没有配置MapAccessor，那么只能用['c']['a']这种解析方式
            context.getPropertyAccessors().add(new MapAccessor());
            // 设置变量
            rootObject.forEach(context::setVariable);
        }

        return String.valueOf(expression.getValue(context));
    }
}

